Transparent, fully reproducible rebuild of the MRD–PET/CT agreement and prognosis analysis using paired 2×2 data. All inputs are harmonised to canonical cell counts (a = MRD−/PET+, b = MRD−/PET−, c = MRD+/PET−, d = MRD+/PET+) with percentages back-calculated to counts by proportional allocation and reconciled to the reported cohort size (n).
Analysis sets: (i) Primary (MRD and PET/CT at the same time-point); (ii) Strict (Δ ≤ 30 days between MRD and PET/CT); (iii) Sensitivity (including reconstructed rows and prespecified exclusions).
Agreement metrics: Per study we report the concordant proportion (b+d)/n, Cohen’s κ (standard error and 95% confidence interval), McNemar mid-p, and Gwet’s AC1 and prevalence- and bias-adjusted κ (PABAK). Study-level κ is pooled using random-effects (restricted maximum likelihood) with Knapp–Hartung adjustment. Directional discordance is defined as log-odds(MRD−/PET+ vs MRD+/PET−) with Haldane–Anscombe correction and synthesised by random-effects meta-analysis; 95% prediction intervals are reported when estimable.
Fréchet–Hoeffding κ bounds: Identification regions are derived per study and for the pooled marginals, with bounds frequently crossing zero, indicating weak identifiability of κ and supporting cautious interpretation and a complementary view of MRD and PET/CT.
Survival (progression-free survival, PFS): Dual-negative (MRD−/PET−) versus all other response categories. Hazard ratios (HR) are pooled using random-effects (restricted maximum likelihood) with Knapp–Hartung, yielding a pooled HR of 0.33 (95% CI 0.24–0.45) for dual-negative versus others; small-study asymmetry tests are interpreted descriptively given the low number of cohorts. Primary pooled matrix (same-time-point set): a = 435 (37.4%), b = 374 (32.1%), c = 304 (26.1%), d = 51 (4.4%); n = 1,164; pooled concordant proportion = 36.5% and pooled κ = −0.26 (95% CI −0.31 to −0.21).
Key outputs: Table 1 (study characteristics); Table 2 (concordance and directional discordance); Figure 2A (pooled κ forest plot); Figure 2B (directional discordance meta-analysis); Figure 3A (PFS meta-analysis); Supplementary Figure SF1 (pooled 2×2 matrix); Supplementary Tables ST1–ST2 (Fréchet–Hoeffding bounds and risk-of-bias).
*Figure S1 omitted: simulation unavailable.**Figure S2 omitted: DCA inputs unavailable.*
*Table S1 omitted: could not compute FH bounds.*
ST1 source: tab2 (in-memory); n=10; cols = Study, n, MRD-/PET+, MRD-/PET-, MRD+/PET-, MRD+/PET+, pct_agree, Kappa, McNemar_p, AC1, AC1_se, AC1_lcl, AC1_ucl, PABAK, PABAK_lcl, PABAK_ucl
.
Supplementary Methods S3 – κ identification intervals (Fréchet–Hoeffding bounds) with point estimate
agg_png
2
B. Directional discordance (PRIMARY) — Lancet style
Figure 2B (STRICT; Δ ≤ 30) — Directional discordance
Supplementary Figure SF1. Concordance matrices for PRIMARY and STRICT time-points. STRICT applies the Δ(MRD–PET) ≤ 30 days rule to treat assessments as paired.
<em>Strict analysis:</em> pooled log-odds = -0.67 (-2.02 – 0.68); I² = 94%
</div>
| Study | logHR | SE | HR | HR_lo | HR_hi |
|---|---|---|---|---|---|
| Alonso R, Cedena MT, Gomez-Grande A, Rios R, Moraleda JM, Cabanas V, Moreno MJ, Lopez-Jimenez J, Martin F, Sanz A, Valeri A, Jimenez A, Sanchez R, Lahuerta JJ, Martinez-Lopez J | -1.509 | 0.237 | 0.221 | 0.139 | 0.352 |
| Zamagni E, Oliva S, Gay F, Capra A, Rota-Scalabrini D, D’Agostino M, Belotti A, Galli M, Racca M, Zambello R, Gamberi B, Albano D, Bertamini L, Versari A, Grasso M, Sgherza N, Priola C, Fioritoni F, Patriarca F, De Cicco G, Villanova T, Pascarella A, Zucchetta P, Tacchetti P, Fanti S, Mancuso K, Barbato S, Boccadoro M, Musto P, Cavo M, Nanni C | -0.799 | 0.342 | 0.450 | 0.230 | 0.880 |
| Mookerjee A, Gupta R, Kumar R, Sharma A, Pandey RM, Kumar L | -1.151 | 0.234 | 0.316 | 0.200 | 0.500 |
| Fonseca R, Arribas M, Wiedmeier-Nutor JE, et al. | -0.564 | 0.485 | 0.569 | 0.220 | 1.470 |
Figure 3. Dual-negative MRD−/PET-CT− versus all other states: pooled hazard ratio for progression-free survival
stopifnot(exists("surv_df"), nrow(surv_df) >= 1)
if (!exists("res_hr") || !inherits(res_hr, "rma.uni")) {
res_hr <- metafor::rma(yi = surv_df$logHR, vi = surv_df$vi, method = "REML", test = "knha")
}
mu <- as.numeric(res_hr$b)
# Limits from data to include ALL studies
x_min <- min(surv_df$logHR, na.rm = TRUE) - 0.15
x_max <- max(surv_df$logHR, na.rm = TRUE) + 0.15
y_max <- max(surv_df$SE, na.rm = TRUE) + 0.02
y_seq <- seq(0, y_max, length.out = 200)
cone_left <- data.frame(x = mu - 1.96*y_seq, y = y_seq)
cone_right <- data.frame(x = mu + 1.96*y_seq, y = y_seq)
p_fun <- ggplot(surv_df, aes(x = logHR, y = SE)) +
geom_line(data = cone_left, aes(x, y), linetype = "dashed") +
geom_line(data = cone_right, aes(x, y), linetype = "dashed") +
geom_vline(xintercept = mu, linetype = "dashed") +
geom_point(size = 2.5) +
ggrepel::geom_label_repel(aes(label = StudyShort),
size = 3, label.size = 0.25, label.r = unit(0.08, "lines"),
min.segment.length = 0, seed = 1234) +
coord_cartesian(xlim = c(x_min, x_max), ylim = c(0, y_max), expand = FALSE) +
labs(x = "log(HR)", y = "SE", title = "B. Funnel plot (log(HR) vs SE)") +
theme_minimal(base_size = 11) +
theme(panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5))
out_png <- here::here("figs","Figure_3B_Funnel.png")
ggsave(out_png, p_fun, width = 7.8, height = 5.0, dpi = 300, bg = "white")
cap <- if (exists("legend_for")) legend_for("Figure_3B_Funnel.png") else
"B. Funnel plot (log(HR) vs SE). Dashed cone = ~95% pseudo-CIs; vertical line = pooled effect; Egger’s p shown; for k<10 interpret with caution."
if (file.exists(out_png)) { if (exists("show_png")) show_png("figs", basename(out_png), caption = cap, width = "95%") else knitr::include_graphics(out_png) }
B. Funnel plot (log(HR) vs SE). Dashed cone = ~95% pseudo-CIs; vertical line = pooled effect. Egger’s test p shown; for k<10 interpret with caution.
| ST2: Egger summary — PFS HR (dual−negative vs others) | |||
| model | k | center_log | Egger_p |
|---|---|---|---|
| Leave-one-out (sorted by |Δ pooled|) | ||||||||||
| Study | Center | LOO_est | LOO_se | Delta | LCL | UCL | HR_center | HR | HR_LCL | HR_UCL |
|---|---|---|---|---|---|---|---|---|---|---|
Baujat plot
x y ids slab
1 1.375083763 0.86581205 1 1
2 0.610551900 0.16896379 2 2
3 0.008443577 0.06043132 3 3
4 1.076756570 0.20138992 4 4
#QUIPS / QUADAS-2: setup, templates, and import
Sensitivity (exclude QUADAS-2 High): κ = 0.182 (0.117–0.246)
NULL
[1] “figs/Supplementary_Figure_QUADAS2_Traffic.png” attr(,“class”) [1]
“knit_image_paths” “knit_asis”
QUIPS overall unavailable or only one level → subgroup meta
skipped.
Clinical pathway schematic mapping 2×2 states to suggested actions.
Supplementary Figures (ZIP) (4.8 MB) Supplementary Tables (ZIP) (52.6 KB) Complete Supplementary Bundle (ZIP) (4.8 MB)
sessionInfo()
R version 4.5.0 (2025-04-11)
Platform: x86_64-apple-darwin20
Running under: macOS Sequoia 15.4.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/Zurich
tzcode source: internal
attached base packages:
[1] grid stats graphics grDevices utils datasets methods base
other attached packages:
[1] fs_1.6.6 patchwork_1.3.0 knitr_1.50 rlang_1.1.6 gt_1.0.0
[6] metasens_1.5-3 rsvg_2.6.2 DiagrammeRsvg_0.1 DiagrammeR_1.0.11 here_1.0.1
[11] ggforce_0.5.0 RColorBrewer_1.1-3 htmltools_0.5.8.1 DT_0.33 zip_2.3.3
[16] htmlwidgets_1.6.4 DescTools_0.99.60 writexl_1.5.4 glue_1.8.0 scales_1.4.0
[21] ggrepel_0.9.6 png_0.1-8 cowplot_1.1.3 broom_1.0.8 janitor_2.2.1
[26] irr_0.84.1 lpSolve_5.6.23 meta_8.1-0 metafor_4.8-0 numDeriv_2016.8-1.1
[31] metadat_1.4-0 Matrix_1.7-3 readxl_1.4.5 lubridate_1.9.4 forcats_1.0.0
[36] stringr_1.5.1 dplyr_1.1.4 purrr_1.0.4 readr_2.1.5 tidyr_1.3.1
[41] tibble_3.2.1 ggplot2_3.5.2 tidyverse_2.0.0
loaded via a namespace (and not attached):
[1] mathjaxr_1.8-0 rstudioapi_0.17.1 jsonlite_2.0.0 magrittr_2.0.3 magick_2.8.7 farver_2.1.2
[7] nloptr_2.2.1 rmarkdown_2.29 ragg_1.4.0 vctrs_0.6.5 minqa_1.2.8 CompQuadForm_1.4.3
[13] curl_6.2.3 haven_2.5.5 cellranger_1.1.0 sass_0.4.10 bslib_0.9.0 rootSolve_1.8.2.4
[19] cachem_1.1.0 commonmark_1.9.5 lifecycle_1.0.4 pkgconfig_2.0.3 R6_2.6.1 fastmap_1.2.0
[25] rbibutils_2.3 snakecase_0.11.1 digest_0.6.37 Exact_3.3 rprojroot_2.0.4 textshaping_1.0.1
[31] crosstalk_1.2.1 labeling_0.4.3 timechange_0.3.0 httr_1.4.7 polyclip_1.10-7 compiler_4.5.0
[37] proxy_0.4-27 bit64_4.6.0-1 withr_3.0.2 backports_1.5.0 MASS_7.3-65 gtools_3.9.5
[43] gld_2.6.7 tools_4.5.0 nlme_3.1-168 generics_0.1.3 gtable_0.3.6 tzdb_0.5.0
[49] class_7.3-23 data.table_1.17.4 lmom_3.2 hms_1.1.3 xml2_1.3.8 pillar_1.10.2
[55] markdown_2.0 vroom_1.6.5 splines_4.5.0 tweenr_2.0.3 lattice_0.22-7 bit_4.6.0
[61] tidyselect_1.2.1 juicyjuice_0.1.0 reformulas_0.4.1 V8_6.0.4 litedown_0.7 xfun_0.52
[67] expm_1.0-0 visNetwork_2.1.2 stringi_1.8.7 yaml_2.3.10 boot_1.3-31 evaluate_1.0.3
[73] robvis_0.3.0 cli_3.6.5 systemfonts_1.2.3 Rdpack_2.6.4 jquerylib_0.1.4 dichromat_2.0-0.1
[79] Rcpp_1.0.14 parallel_4.5.0 lme4_1.1-37 mvtnorm_1.3-3 e1071_1.7-16 crayon_1.5.3